#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/version.h>

#define DEVICE_NAME "dma_mmap"
#define PAGE_COUNT 3
#define BUFFER_SIZE (PAGE_COUNT * PAGE_SIZE)

static int major;
static void *dma_buffer;
static dma_addr_t dma_handle;
static struct class *dev_class;
static struct device *char_dev;
static struct platform_device *dma_plat_dev;

/* DMA缓冲区同步函数：确保CPU和设备数据一致性 */
static void sync_dma_buffer(size_t size, enum dma_data_direction dir)
{
	if (dma_plat_dev) {
		dma_sync_single_for_cpu(&dma_plat_dev->dev, dma_handle, size,
					dir);
	}
}

/* 打开设备时同步DMA缓冲区（从设备到CPU） */
static int dma_mmap_open(struct inode *inode, struct file *filp)
{
	sync_dma_buffer(BUFFER_SIZE, DMA_FROM_DEVICE);
	return 0;
}

/* 关闭设备时同步DMA缓冲区（从CPU到设备） */
static int dma_mmap_release(struct inode *inode, struct file *filp)
{
	sync_dma_buffer(BUFFER_SIZE, DMA_TO_DEVICE);
	return 0;
}

/* 实现mmap：将DMA内存映射到用户空间 */
static int dma_mmap(struct file *filp, struct vm_area_struct *vma)
{
	unsigned long map_size = vma->vm_end - vma->vm_start;

	/* 检查映射大小是否超过缓冲区 */
	if (map_size > BUFFER_SIZE) {
		return -EINVAL;
	}

	/* 设置非缓存属性（DMA内存通常需要非缓存） */
	// vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
	vm_flags_set(vma, VM_READ | VM_WRITE | VM_SHARED);

	/* 映射物理地址到用户虚拟地址 */
	if (remap_pfn_range(vma, vma->vm_start, dma_handle >> PAGE_SHIFT,
			    map_size, vma->vm_page_prot)) {
		return -EAGAIN;
	}

	printk(KERN_INFO
	       "DMA memory mapped: user virt 0x%lx (size %lu bytes)\n",
	       vma->vm_start, map_size);
	return 0;
}

/* 文件操作集合 */
static const struct file_operations dma_fops = {
	.owner = THIS_MODULE,
	.open = dma_mmap_open,
	.release = dma_mmap_release,
	.mmap = dma_mmap,
};

/* 模块初始化函数 */
static int __init dma_mmap_init(void)
{
	int ret;

	/* 1. 注册字符设备 */
	major = register_chrdev(0, DEVICE_NAME, &dma_fops);
	if (major < 0) {
		printk(KERN_ERR "Failed to register char device (err %d)\n",
		       major);
		return major;
	}

	/* 2. 创建设备类 */
	dev_class = class_create(DEVICE_NAME "_class");
	if (IS_ERR(dev_class)) {
		ret = PTR_ERR(dev_class);
		printk(KERN_ERR "Failed to create class (err %d)\n", ret);
		goto unregister_chrdev;
	}

	/* 3. 创建设备节点（/dev/dma_mmap） */
	char_dev = device_create(dev_class, NULL, MKDEV(major, 0), NULL,
				 DEVICE_NAME);
	if (IS_ERR(char_dev)) {
		ret = PTR_ERR(char_dev);
		printk(KERN_ERR "Failed to create device node (err %d)\n", ret);
		goto destroy_class;
	}

	/* 4. 创建并注册平台设备（用于DMA分配） */
	dma_plat_dev = platform_device_alloc(DEVICE_NAME "_plat", -1);
	if (!dma_plat_dev) {
		printk(KERN_ERR "Failed to allocate platform device\n");
		ret = -ENOMEM;
		goto destroy_device;
	}

	/* 初始化DMA掩码（必须设置，告知内核设备支持的DMA地址宽度） */
	dma_plat_dev->dev.coherent_dma_mask = DMA_BIT_MASK(64);
	dma_plat_dev->dev.dma_mask = &dma_plat_dev->dev.coherent_dma_mask;

	/* 注册平台设备 */
	ret = platform_device_add(dma_plat_dev);
	if (ret) {
		printk(KERN_ERR "Failed to register platform device (err %d)\n",
		       ret);
		goto put_platform_device;
	}

	/* 5. 分配DMA连续内存 */
	dma_buffer = dma_alloc_coherent(&dma_plat_dev->dev, BUFFER_SIZE,
					&dma_handle, GFP_KERNEL);
	if (!dma_buffer) {
		printk(KERN_ERR "Failed to allocate DMA buffer\n");
		ret = -ENOMEM;
		goto unregister_platform;
	}

	/* 初始化缓冲区为0 */
	memset(dma_buffer, 0, BUFFER_SIZE);
	printk(KERN_INFO
	       "DMA buffer initialized: virt=0x%p, phys=0x%pad, size=%u bytes\n",
	       dma_buffer, &dma_handle, BUFFER_SIZE);

	return 0;

	/* 错误处理：按分配逆序释放资源 */
unregister_platform:
	platform_device_unregister(dma_plat_dev);
put_platform_device:
	platform_device_put(dma_plat_dev);
destroy_device:
	device_destroy(dev_class, MKDEV(major, 0));
destroy_class:
	class_destroy(dev_class);
unregister_chrdev:
	unregister_chrdev(major, DEVICE_NAME);
	return ret;
}

/* 模块卸载函数 */
static void __exit dma_mmap_exit(void)
{
	/* 释放DMA内存 */
	if (dma_buffer && dma_plat_dev) {
		dma_free_coherent(&dma_plat_dev->dev, BUFFER_SIZE, dma_buffer,
				  dma_handle);
		printk(KERN_INFO "DMA buffer freed\n");
	}

	/* 注销平台设备 */
	if (dma_plat_dev) {
		platform_device_unregister(dma_plat_dev);
	}

	/* 销毁设备节点和类 */
	if (!IS_ERR(char_dev)) {
		device_destroy(dev_class, MKDEV(major, 0));
	}
	if (!IS_ERR(dev_class)) {
		class_destroy(dev_class);
	}

	/* 注销字符设备 */
	unregister_chrdev(major, DEVICE_NAME);

	printk(KERN_INFO "DMA mmap module unloaded\n");
}

module_init(dma_mmap_init);
module_exit(dma_mmap_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DMA Coherent Memory Mapping Module");
MODULE_AUTHOR("Your Name");
